/** CS2340 Spring 2011 Project
 *  Team: 40 Jiggawatt Hard Driver
 *  Suk Hwan Hong
 *  Rhys Saraceni
 *  Travis Cooper
 *  Luke Stubb
 *
 * @author 40 Jiggawatt Hard Driver
 * @version 1.0 
 */

// $codepro.audit.disable com.instantiations.assist.eclipse.analysis.audit.rule.effectivejava.alwaysOverridetoString.alwaysOverrideToString, numericLiterals
package edu.gatech.jiggawatt.office.presenter;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;
import edu.gatech.jiggawatt.office.model.Admin;
import edu.gatech.jiggawatt.office.model.Appointment;
import edu.gatech.jiggawatt.office.model.DailySchedule;
import edu.gatech.jiggawatt.office.model.Date;
import edu.gatech.jiggawatt.office.model.DoctorsOrder;
import edu.gatech.jiggawatt.office.model.IncomeStatistics;
import edu.gatech.jiggawatt.office.model.Patient;
import edu.gatech.jiggawatt.office.model.Nurse;
import edu.gatech.jiggawatt.office.model.Doctor;
import edu.gatech.jiggawatt.office.model.Time;
import edu.gatech.jiggawatt.office.model.TreatmentRecord;
import edu.gatech.jiggawatt.office.model.User;
import edu.gatech.jiggawatt.office.model.Database;
import edu.gatech.jiggawatt.office.model.YearlySchedule;

/**
 * Represents "presenter"
 * It coordinates the interactions between client and server
 * It also manages all the GUI coordinations within client
 * @author 40 Jiggawatt Hard Driver
 * @version 1.0 
 */
public class Presenter { // $codepro.audit.disable declareDefaultConstructors

	/**
	 * Field serverIP.
	 */
	private String serverIP; // $codepro.audit.disable variableShouldBeFinal
	
	/**
	 * Field serverPort.
	 */
	private String serverPort; // $codepro.audit.disable variableShouldBeFinal
	
	/**
	 * Field database.
	 */
	private Database database; // $codepro.audit.disable variableShouldBeFinal
	
	private FileWriter outFile;
	private PrintWriter out;
	private String logLocation = "log.txt"; // $codepro.audit.disable stringLiterals
	private java.util.Date logDate;
	private DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd 'at' h:mm a"); // $codepro.audit.disable stringLiterals
	
	/**
	 * Constructor for Presenter.
	 * @param serverIP String
	 * @param serverPort String
	 */
	public Presenter(String serverIP, String serverPort) {
		this.serverIP = serverIP;
		this.serverPort = serverPort;
		database = new Database();
		
		try
		{
			outFile = new FileWriter(logLocation, true); // $codepro.audit.disable closeWhereCreated
			out = new PrintWriter(outFile); // $codepro.audit.disable closeWhereCreated
		}
		catch (IOException e)
		{
			System.out.println("Failed to open log file"); // $codepro.audit.disable debuggingCode
		}		
		
		//Default users for demo purpose
		database.addUser(new Admin("Admin", "1234", "Bob", "Waters")); // $codepro.audit.disable stringLiterals
		database.addUser(new Patient("Patient", "1234", "Suk Hwan", "Hong")); // $codepro.audit.disable stringLiterals
		database.addUser(new Nurse("Nurse", "1234", "Travis", "Cooper")); // $codepro.audit.disable stringLiterals
		database.addUser(new Doctor("Doctor", "1234", "Rhys", "Saraceni")); // $codepro.audit.disable stringLiterals
		database.addUser(new Patient("Patient2", "1234", "Luke", "Stubbs")); // $codepro.audit.disable stringLiterals
		database.addUser(new Doctor("Doctor2", "1234", "Good", "Doctor")); // $codepro.audit.disable stringLiterals
		database.addUser(new Doctor("Doctor3", "1234", "Better", "Doctor")); // $codepro.audit.disable stringLiterals
		database.addUser(new Doctor("Doctor4", "1234", "Best", "Doctor")); // $codepro.audit.disable stringLiterals
	}

	/**
	 * Returns -1 if fails, userType if it succeeds, or 4 if account is locked
	 * @param id String
	 * @param pw String
	 * @return int
	 */
	public int login(String id, String pw) {
		final User user = database.getUser(id);
		logDate = new java.util.Date();
		out.println(id + " trying to log in at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		
		if (null == user) {

			return -1;
		}
		
		if (user.isSuspended()) {
			return 4;
		}
		
		if (true == user.checkPassword(pw)) { // $codepro.audit.disable equalityTestWithBooleanLiteral
			user.clearFailedLogin();
			return user.getUserType();
		}
		
		user.incFailedLogin();
		return -1;
	}
	
	/**
	 * add user to the database
	 * @param user User
	 * @return true if success, false otherwise
	 */
	public boolean addUser(User user) { // $codepro.audit.disable booleanMethodNamingConvention
		logDate = new java.util.Date();
		out.println("adding user at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.addUser(user);
	}
	
	/**
	 * get user from the database
	 * @param id String
	 * @return User
	 */
	public User getUser(String id) {
		logDate = new java.util.Date();
		out.println("getting user at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.getUser(id);
	}
	
	/**
	 * updates user
	 * @param updatedUser
	 * @return true if success, false otherwise
	 */
	public boolean updateUser(User updatedUser) // $codepro.audit.disable booleanMethodNamingConvention
	{
		out.println("updating user at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.updateUser(updatedUser);
	}
	
	/**
	 * Sets password of user
	 * @param id
	 * @param newPassword
	 * @return true if success, false otherwise
	 */
	public boolean setPassword(String id, String newPassword) // $codepro.audit.disable booleanMethodNamingConvention
	{
		out.println("setting password of user " + id + " at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.setPassword(id, newPassword);
	}
	
	/**
	 * remove user from the database
	 * @param id String
	 * @return true if success, false otherwise
	 */
	public boolean removeUser(String id) { // $codepro.audit.disable booleanMethodNamingConvention
		out.println("removing user " + id + " at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.removeUser(id);
	}
	
	/**
	 * get list of admins from database
	 * @return list of admins
	 */
	public List<Admin> getAdminList() {
		out.println("getting admin lists at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.getAdminList();
	}
	
	/**
	 * gets list of patients from database
	 * @return list of patients
	 */
	public List<Patient> getPatientList() {
		out.println("getting patient lists at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.getPatientList();
	}
	
	/**
	 * gets list of nurses from database
	 * @return list of nurses
	 */
	public List<Nurse> getNurseList() {
		out.println("getting nurse lists at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.getNurseList();
	}
	
	/**
	 * gets list of doctors from database
	 * @return list of doctors
	 */
	public List<Doctor> getDoctorList() {
		out.println("getting doctor lists at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.getDoctorList();
	}
	
	/**
	 * get patient from database
	 * @param fullName
	 * @return patient retrieved
	 */
	public Patient getPatient(String fullName) {
		out.println("getting patient by name " + fullName + " at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.getPatient(fullName);
	}
	
	/**
	 * adds appointment
	 * @param appointment
	 * @return true if success, false otherwise
	 */
	public boolean addAppointment(Appointment appointment) // $codepro.audit.disable booleanMethodNamingConvention
	{
		out.println("adding appointment at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.addAppointment(appointment);
	}
	
	/**
	 * removes appointment
	 * @param date
	 * @param time
	 * @return true if success, false otherwise
	 */
	public boolean removeAppointment(Date date, Time time) // $codepro.audit.disable booleanMethodNamingConvention
	{
		out.println("removing appointment at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.removeAppointment(date, time);
	}
	
	/**
	 * changes appointment
	 * @param oldApp
	 * @param newApp
	 * @return true if success, false otherwise
	 */ 
	public boolean changeAppointment(Appointment oldApp, Appointment newApp) // $codepro.audit.disable booleanMethodNamingConvention
	{
		out.println("changing appointment at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.changeAppointment(oldApp, newApp);
	}
	
	/**
	 * gets appointment
	 * @param date
	 * @param time
	 * @return appointment retrieved
	 */
	public Appointment getAppointment(Date date, Time time)
	{
		out.println("getting user at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.getAppointment(date, time);
	}
	
	/**
	 * adds treatment record
	 * @param id
	 * @param tr
	 * @return true if success, false otherwise
	 */
	public boolean addTreatmentRecord(String id, TreatmentRecord tr) // $codepro.audit.disable booleanMethodNamingConvention
	{
		out.println("adding treatment record at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.addTreatmentRecord(id, tr);
	}
	
	/**
	 * removes treatment record
	 * @param id
	 * @param tr
	 * @return true if success, false otherwise
	 */
	public boolean removeTreatmentRecord(String id, TreatmentRecord tr) // $codepro.audit.disable booleanMethodNamingConvention
	{
		out.println("removing treatment record at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.removeTreatmentRecord(id, tr);
	}
	
	/**
	 * changes treatment record
	 * @param id
	 * @param oldtr
	 * @param tr
	 * @return true if success, false otherwise
	 */
	public boolean changeTreatmentRecord(String id, TreatmentRecord oldtr, TreatmentRecord tr) // $codepro.audit.disable booleanMethodNamingConvention
	{
		out.println("changing treatment record at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.changeTreatmentRecord(id, oldtr, tr);
	}
	
	/**
	 * gets treatment record
	 * @param id
	 * @param date
	 * @param time
	 * @return treatment record retrieved
	 */
	public TreatmentRecord getTreatmentRecord(String id, Date date, Time time)
	{
		out.println("getting treatment record at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.getTreatmentRecord(id, date, time);
	}
	
	/**
	 * sets doctor's order
	 * @param id
	 * @param date
	 * @param time
	 * @param order
	 * @return true if success, false otherwise
	 */
	public boolean setDoctorsOrder(String id, Date date, Time time, DoctorsOrder order) // $codepro.audit.disable booleanMethodNamingConvention
	{
		out.println("setting doctors order at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.setDoctorsOrder(id, date, time, order);
	}
	
	/**
	 * gets list of treatment records
	 * @param id
	 * @return list of treatment records
	 */
	public List<TreatmentRecord> getTreatmentRecords(String id)
	{
		out.println("getting treatment records at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.getTreatmentRecords(id);
	}
	
	
	/**
	 * gets list of yearly schedules
	 * @return list of yearly schedules
	 */
	public YearlySchedule[] getYearlySchedules()
	{
		out.println("getting yearly schedules at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.getYearlySchedules();
	}
	
	/**
	 * gets daily schedule
	 * @param date
	 * @return daily schedule
	 */
	public DailySchedule getDailySchedule(Date date)
	{
		out.println("getting daily schedule at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.getDailySchedule(date);
	}
	
	/**
	 * gets base year of office calendar
	 * @return base year of office calendar
	 */
	public int getBaseYear()
	{
		out.println("getting base year at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.getBaseYear();
	}
	
	/**
	 * gets max year of office calendar
	 * @return max year of office calendar
	 */
	public int getMaxYear()
	{
		out.println("getting max year at " + dateFormat.format(logDate)); // $codepro.audit.disable stringLiterals
		out.flush();
		return database.getMaxYear();
	}
	
	/**
	 * gets income statistics of specified year and month
	 * @param year
	 * @param month
	 * @return income statistics of specified year and month
	 */
	public IncomeStatistics getIncomeStatistics(int year, int month)
	{
		out.println("getting income statistics of " + year + "-" + month + " " + dateFormat.format(logDate));
		out.flush();
		return database.getIncomeStatistics(year, month);
	}
	
	/**
	 * saves database
	 * @param filename file to save
	 */
	public void save(String filename) {
		try {
			out.println("saving database at " + dateFormat.format(logDate));
			out.flush();
			
			final ObjectOutputStream out = new ObjectOutputStream( // $codepro.audit.disable closeWhereCreated
					new FileOutputStream(filename));
			out.writeObject(database);
		} catch (FileNotFoundException e) {
			out.println("FAILED TO OPEN FILE " + filename + " at " + dateFormat.format(logDate));
			out.flush();
		} catch (IOException e) {
			out.println("SAVING DATABASE FAILED FOR UNKNOWN REASON at " + dateFormat.format(logDate));
			out.flush();
		}
	}
	
	/**
	 * loads database
	 * @param filename file to load
	 */
	public void load(String filename) {
		try {
			out.println("loading database at " + dateFormat.format(logDate));
			out.flush();
			final ObjectInputStream in = new ObjectInputStream( // $codepro.audit.disable closeWhereCreated
					new FileInputStream(filename));
			database = (Database) in.readObject();
		} catch (FileNotFoundException e) {
			out.println("FAILED TO OPEN FILE " + filename + " at " + dateFormat.format(logDate));
			out.flush();
		} catch (IOException e) {
			out.println("FAILED TO OPEN FILE " + filename + " at " + dateFormat.format(logDate));
			out.flush();
		} catch (ClassNotFoundException e) {
			out.println("FAILED TO OPEN FILE " + filename + " at " + dateFormat.format(logDate));
			out.flush();
		}
	}
}
